//!@author denglibin
//!@date 2025/11/3 16:30
//!@desc

use mongodb::bson::{doc, Bson, Document};
use mongodb::options::{ClientOptions, Credential, ServerApi, ServerApiVersion};
use mongodb::Client;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use tokio_stream::StreamExt;

pub struct MongoClient {
    client: Client,
}

impl MongoClient {
    pub async fn new(
        uri: &str, //"mongodb://192.168.6.170:27017";
        username: Option<String>,
        password: Option<String>,
    ) -> Result<MongoClient, Box<dyn std::error::Error>> {
        let mut client_options = ClientOptions::parse(uri).await?;
        if let Some(u) = username {
            if let Some(p) = password {
                //账号密码
                let default_cred = Credential::builder().username(u).password(p).build();
                client_options.credential = Some(default_cred);
            }
        }
        // Set the server_api field of the client_options object to Stable API version 1
        let server_api = ServerApi::builder().version(ServerApiVersion::V1).build();
        client_options.server_api = Some(server_api);
        // 👇 禁用 server_api 设置（让驱动兼容旧版 MongoDB）
        client_options.server_api = None;

        // Create a new client and connect to the server
        let client = Client::with_options(client_options)?;
        Ok(MongoClient { client })
    }

    ///数据库列表
    pub async fn list_database_names(&self) -> Result<Vec<String>, Box<dyn std::error::Error>> {
        let db_list = self.client.list_database_names().await?;
        Ok(db_list)
    }
    ///集合列表
    pub async fn list_collection_names(
        &self,
        db_name: &str,
    ) -> Result<Vec<String>, Box<dyn std::error::Error>> {
        let db = self.client.database(db_name);
        let coll_list = db.list_collection_names().await?;
        Ok(coll_list)
    }
    ///统计数量
    pub async fn count(
        &self,
        db_name: &str,
        collection_name: &str,
        doc: Document, //过滤条件
    ) -> Result<u64, Box<dyn std::error::Error>> {
        // 获取数据库和集合
        let db = self.client.database(db_name);
        let collection = db.collection::<Document>(collection_name);

        // 统计所有文档数量（带过滤条件）
        let count = collection.count_documents(doc).await?;

        Ok(count)
    }
    ///计数，直接读取集合元数据
    pub async fn count_by_metadata(
        &self,
        db_name: &str,
        collection_name: &str,
    ) -> Result<u64, Box<dyn std::error::Error>> {
        // 获取数据库和集合
        let db = self.client.database(db_name);
        let collection = db.collection::<Document>(collection_name);
        let count = collection.estimated_document_count().await?;

        Ok(count)
    }

    ///添加一篇文档，返回id
    pub async fn insert_one_doc<T>(
        &self,
        db_name: &str,
        collection_name: &str,
        doc: T,
    ) -> Result<Bson, Box<dyn std::error::Error>>
    where
        T: Serialize + Send + Sync,
    {
        let r = self
            .client
            .database(db_name)
            .collection(collection_name)
            .insert_one(doc)
            .await?;

        Ok(r.inserted_id)
    }

    ///添加多篇文档
    pub async fn insert_many<T>(
        &self,
        db_name: &str,
        collection_name: &str,
        doc_list: Vec<T>,
    ) -> Result<HashMap<usize, Bson>, Box<dyn std::error::Error>>
    where
        T: Serialize + Send + Sync,
    {
        let r = self
            .client
            .database(db_name)
            .collection(collection_name)
            .insert_many(doc_list)
            .await?;
        Ok(r.inserted_ids)
    }

    ///查询
    pub async fn query_list<T, F>(
        &self,
        db_name: &str,
        collection_name: &str,
        query: Document,
        mut f: F,
    ) -> Result<(), Box<dyn std::error::Error>>
    where
        T: for<'de> Deserialize<'de> + Unpin + Send + Sync,
        F: FnMut(T) -> bool

    {
        // let query = doc! { "name": "pear" };
        // let mut query = Document::new();
        // query.insert("name", "pear");
        log::info!("query: {:?}", query);

        let coll = self
            .client
            .database(db_name)
            .collection::<T>(collection_name);
        let mut cursor = coll.find(query).sort(doc! {"_id": 1}).await?;
        while let Some(doc) = cursor.try_next().await? {
            let b = f(doc);
            if !b {
                break;
            }
        }
        Ok(())
    }
}
